/*
 * @Author: xiaosihan
 * @Date: 2021-04-10 02:44:01
 * @Last Modified by: xiaosihan
 * @Last Modified time: 2022-06-22 19:44:23
 */

import threeUtils from "./threeUtils";

function bezier(arr: [number, number, number, number], t: number) {
    const ut = 1 - t;
    const a1 = arr[1] * ut + arr[2] * t;
    const result = ((arr[0] * ut + arr[1] * t) * ut + a1 * t) * ut + (a1 * ut + (arr[2] * ut + arr[3] * t) * t) * t;
    return result;
}

export type TransitionParame = {
    // [key: string]: number | Vector2 | Vector3 | Color | `#${string}`
    [key: string]: number
}

// 过渡动画插件
export default class Transition<T extends TransitionParame, K extends Extract<keyof T, string>> {

    /**
     *Creates an instance of transition.
     * @param {{ [key: string]: number }} // 初始值
     * @param {[0.8, 0, 0.2, 1]} // 贝塞尔曲线
     * @param {number} [duration=1000]  // 过渡时间
     * @memberof transition
     */
    constructor(
        param: T,
        bezier: [number, number, number, number] = [0.8, 0, 0.2, 1],
        duration = 500
    ) {

        this.setDuration(duration);

        this.setBezier(bezier);

        this.reset(param);
    }

    // 所有的 key 的枚举
    _keys: Array<Extract<keyof T, string>> = [];

    // 贝塞尔曲线
    _bezier: [number, number, number, number] = [0.8, 0, 0.2, 1];

    // x轴贝塞尔
    _Xbezier: [number, number, number, number] = [0, 0.8, 0.2, 1];

    // y轴贝塞尔
    _Ybezier: [number, number, number, number] = [0, 0, 1, 1];

    setBezier(bezier: [number, number, number, number] = [0.8, 0, 0.2, 1]): this {
        this._bezier = [...bezier];
        this._Xbezier = [0, bezier[0], bezier[2], 1];
        this._Ybezier = [0, bezier[1], bezier[3], 1];
        return this;
    }

    // 当前时间戳
    timeTemp = performance.now();

    // 过渡时间
    _duration = 0;

    //延时
    _delay = 0;

    // 设置过渡时间
    setDuration(duration = 500): this {
        setTimeout(() => {
            this._duration = duration;
        }, 0);
        return this;
    }

    // 设置延时
    setDelay(delay = 0) {
        this._delay = delay;
        return this;
    }

    // 存储 每个参数的 起始位置 起始时间 终止位置  终止时间
    _: { [key in `${K}_start` | `${K}_end` | `${K}_startTime`]: number } = {} as { [key in `${K}_start` | `${K}_end` | `${K}_startTime`]: number };

    // 数据改变的事件回调
    private onChangeCall = (josn: T) => { };

    //结束的事件回调
    private onDoneCall = (josn: T) => { };

    // 执行过度动画
    run({ timeTemp }: any) {
        this.timeTemp = timeTemp;

        this.onChangeCall(this.getJson());

        if (this.isDone()) {
            this.onDoneCall(this.getJson());
            threeUtils.removeEventListener("requestAnimationFrame", this.runbindThis);
        }

        return this;
    }

    runbindThis = this.run.bind(this);

    // 设置动画对象的回调事件
    onChange(onChangeCall: (josn: T) => void): this {
        this.onChangeCall = onChangeCall;
        this.onChangeCall(this.getJson());
        return this;
    }

    //设置动画对象的
    onDone(onDoneCall: (josn: T) => void): this {
        this.onDoneCall = onDoneCall;
        return this;
    }

    // 计算当前 key 的值
    get(key: Extract<keyof T, string>): T[K] {

        key = String(key) as Extract<keyof T, string>;

        if (this._keys.includes(key) && this.timeTemp < ((this._ as any)[key + "_startTime"] + this._duration)) {

            let start = (this._ as any)[key + "_start"];

            let end = (this._ as any)[key + "_end"];

            let timeProgress = Math.min(1, Math.max(0, (this.timeTemp - (this._ as any)[key + "_startTime"]) / this._duration));

            // 斜率
            let t = 0;

            // 微分法 求斜率t
            while (bezier(this._Xbezier, t + 0.1) < timeProgress) {
                t += 0.1;
            }
            while (bezier(this._Xbezier, t + 0.01) < timeProgress) {
                t += 0.01;
            }
            while (bezier(this._Xbezier, t + 0.001) < timeProgress) {
                t += 0.001;
            }
            while (bezier(this._Xbezier, t + 0.0001) < timeProgress) {
                t += 0.0001;
            }

            t = Math.min(1, Math.max(0, t))  // 斜率最大为1

            // 通过斜率求y值
            return (bezier(this._Ybezier, t) * (end - start)) + start;

        } else if (this._keys.includes(key) && this.timeTemp >= ((this._ as any)[key + "_startTime"] + this._duration)) {

            return (this._ as any)[key + "_end"];
        }

        // `没有 ${ key } 的值`;
        return 0 as T[K];
    }

    async set(param: Partial<T>): Promise<this> {

        let timetemp = performance.now() + this._delay;;

        for (let key in param) {

            // 有就赋值
            if (this._keys.includes(key) && Math.abs(param[key] - (this._ as any)[key + "_end"]) > 0.0001) {

                (this._ as any)[key + "_start"] = this.get(key);

                (this._ as any)[key + "_end"] = param[key];
                // 起始时间
                (this._ as any)[key + "_startTime"] = timetemp;

                // 执行状态未完成
                this._isDone = false;

                // 没有就添加一个
            } else if (!this._keys.includes(key)) {

                (this._ as any)[key + "_start"] = param[key];

                (this._ as any)[key + "_end"] = param[key];
                // 起始时间
                (this._ as any)[key + "_startTime"] = timetemp - this._duration;

                // 执行状态未完成
                this._isDone = false;
            }

            if (!this._keys.includes(key)) {
                this._keys.push(key);
            }

            // // 执行状态未完成
            // this._isDone = false;

        }

        if (!this._isDone) {
            threeUtils.removeEventListener("requestAnimationFrame", this.runbindThis);
            threeUtils.addEventListener("requestAnimationFrame", this.runbindThis);
            await new Promise(resolve => setTimeout(resolve, 50));
            await new Promise(resolve => setTimeout(resolve, this._duration));
        }

        return this;
    }
    // 重置起始和终止位置
    reset(param: Partial<T>): this {

        for (let key in param) {

            if (!this._keys.includes(key)) {
                this._keys.push(key);
            }

            (this._ as any)[key + "_start"] = param[key];
            (this._ as any)[key + "_end"] = param[key];
            (this._ as any)[key + "_startTime"] = -this._duration;
        }

        if (this.onChangeCall) {
            this.onChangeCall(this.getJson());
            this.onDoneCall(this.getJson());
        }

        this._isDone = true;
        return this;
    }

    // 获取过渡对象上面的key
    getKeys(): Array<Extract<keyof T, string>> {
        return [...this._keys];
    }

    // 判断是否有这个参数
    hasKey(key: Extract<keyof T, string>): boolean {
        return this._keys.includes(key);
    }

    // 获取全部参数
    getJson(): T {
        let keys = this.getKeys();

        let json: T = {} as T;

        keys.map(key => {
            json[key] = this.get(key);
        });
        return json;
    }

    _isDone = false;

    // 是否执行完成了
    isDone(): boolean {

        if (this._isDone) {
            return true;
        }

        let isisDone = false;

        for (let i in this._keys) {

            isisDone = (this._ as any)[this._keys[i] + "_startTime"] + this._duration < this.timeTemp;

            // 只要有一个没有完成 都算没有完成 过渡
            if (!isisDone) {
                return false;
            }
        }

        if (!this._isDone) {
            this._isDone = true;
            return false;
        }

        return true;
    }
}